﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using nwrole.lib;
using nwrole.plugin;

namespace nwrole.Azure {
    public class AzurePluginProvider : PluginProviderBase {
        #region IPluginProvider

        public AzurePluginProvider() { }

        public override PropertyList LoadPluginSettings() {
            CloudBlobContainer blobContainer = GetBlobContainer("services");

            string filename = this.ServiceName + ".zip";

            var blob = blobContainer.GetBlobReference(filename);
            blob.FetchAttributes();

            string settings = blob.Metadata["PluginSettings"];
            if (settings == null)
                return null;

            PropertyList plist = PropertyList.LoadData(System.Web.HttpUtility.HtmlDecode(settings));

            return plist;
        }

        public override void SavePluginSettings(PropertyList plist) {

            CloudBlobContainer blobContainer = GetBlobContainer("services");

            string filename = this.ServiceName + ".zip";

            var blob = blobContainer.GetBlobReference(filename);
            blob.FetchAttributes();

            string text = plist.ToXml();

            blob.Metadata["PluginSettings"] = System.Web.HttpUtility.HtmlEncode(text);

            blob.SetMetadata();
        }

        public override void RefreshPlugins() {
            string applicationDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string servicesDir = Path.Combine(applicationDirectory, "services");
            string serviceDir = Path.Combine(servicesDir, this.ServiceName);

            var catalog = new AggregateCatalog();
            var container = new CompositionContainer(catalog);
            var batch = new CompositionBatch();
            batch.AddPart(this);
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            //catalog.Catalogs.Add(new DirectoryCatalog(ServicePath));
            catalog.Catalogs.Add(new DirectoryCatalog(serviceDir));
            container.Compose(batch);
        }

        public override void Initialize() {
          GetProviderSettings();

          base.Initialize();
        }

        #endregion

        private CloudBlobClient GetBlobClient() {
            var storageAccount = CloudStorageAccount.Parse(this.AzureStorageConnectionString);
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            // For large file copies you need to set up a custom timeout period
            // and using parallel settings appears to spread the copy across multiple threads
            // if you have big bandwidth you can increase the thread number below
            // because Azure accepts blobs broken into blocks in any order of arrival.
            blobClient.Timeout = new System.TimeSpan(1, 0, 0);
            blobClient.ParallelOperationThreadCount = 2;

            return blobClient;
        }
        private CloudBlobContainer GetBlobContainer(string name) {

            CloudBlobClient blobClient = GetBlobClient();
            
            // Get and create the container
            CloudBlobContainer blobContainer = blobClient.GetContainerReference(name);
            blobContainer.CreateIfNotExist();

            // Setup the permissions on the container to be private
            var permissions = new BlobContainerPermissions();
            permissions.PublicAccess = BlobContainerPublicAccessType.Off;
            blobContainer.SetPermissions(permissions);

            return blobContainer;
        }

        private void UnpackZip(Stream stream, string destPath) {
            
            //string StorageDirectoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            //Directory.CreateDirectory(StorageDirectoryPath);

            using (ZipInputStream zipStream = new ZipInputStream(stream)) {
                ZipEntry entry;
                while ((entry = zipStream.GetNextEntry()) != null) {
                    /*if (!String.IsNullOrEmpty(entry.Name) && entry.Name.Equals(@"solution.plist", StringComparison.OrdinalIgnoreCase)) {
                        MemoryStream dataStream = new MemoryStream();
                        byte[] buffer = new byte[8192];
                        int size = zipStream.Read(buffer, 0, buffer.Length);
                        while (size > 0) {
                            dataStream.Write(buffer, 0, size);
                            size = zipStream.Read(buffer, 0, buffer.Length);
                        };
                        dataStream.Seek(0, SeekOrigin.Begin);
                        PropertyList plist = new PropertyList(dataStream);
                        Id = new Guid(plist.StringForKey("id"));
                        Name = plist.StringForKey("name");
                        Description = plist.StringForKey("description");
                        Author = plist.StringForKey("author");
                        Created = plist.DateTimeForKey("created");
                        Updated = plist.DateTimeForKey("updated");
                        foreach (object dict in plist.ArrayForKey("services"))
                            Services.Add(new Service((IDictionary)dict));
                    } else*/ {
                                 using (FileStream fileStream = File.Create(Path.Combine(destPath, entry.Name))) {
                            byte[] buffer = new byte[8192];
                            int size;
                            while ((size = zipStream.Read(buffer, 0, buffer.Length)) > 0)
                                fileStream.Write(buffer, 0, size);
                            fileStream.Close();
                        }
                        //Files.Add(Path.Combine(StorageDirectoryPath, entry.Name));
                    }
                }
            }
        }

        private void GetProviderSettings() {
          using (var supervisor = new TcpChannel<ISupervisorContract>("localhost:" + this.SupervisorPort + "/Supervisor")) {
            var pluginProviderProperties = supervisor.Channel.GetPluginProviderSettings();

            foreach (var val in pluginProviderProperties) {
              var prop = this.GetType().GetProperty(val.Key);
              if (prop != null) {
                prop.SetValue(this, val.Value, new object[0]);
              }
            }
          }
        }

        /*private string ServicePath {
            get {
                return Path.Combine(Configuration.ServicesDirectory, serviceName);
            }
        }*/

        public string AzureStorageConnectionString { get; set; }
    }
}
